#pragma once

#include "cuVector.cuh"
#include "raycast/raycast.cuh"
#include "raycast/math.hpp"
struct Matrix4
{
    float4 data[4];

    inline __host__ __device__ float3 get_translation() const
    {
        return make_float3(data[0].w, data[1].w, data[2].w);
    }
};

inline __device__ uint2 thr2pos2()
{
// #ifdef __CUDACC__
    return make_uint2(threadIdx.x, blockIdx.x);
// #else
//     return make_uint2(0);
// #endif
}

std::ostream &operator<<(std::ostream &out, const Matrix4 &m);
Matrix4 operator*(const Matrix4 &A, const Matrix4 &B);
Matrix4 inverse(const Matrix4 &A);

inline __host__ __device__ float4 operator*(const Matrix4 &M, const float4 &v)
{
    return make_float4(dot<float4, float4>(M.data[0], v), dot(M.data[1], v), dot(M.data[2], v), dot(M.data[3], v));
}

// inline __host__ __device__ float3 operator*(const Matrix4 &M, const float3 &v)
// {
//     return make_float3(
//         dot(make_float3(M.data[0]), v) + M.data[0].w,
//         dot(make_float3(M.data[1]), v) + M.data[1].w,
//         dot(make_float3(M.data[2]), v) + M.data[2].w);
// }

// inline __host__ __device__ float3 rotate(const Matrix4 &M, const float3 &v)
// {
//     return make_float3(
//         dot(make_float3(M.data[0]), v),
//         dot(make_float3(M.data[1]), v),
//         dot(make_float3(M.data[2]), v));
// }

inline Matrix4 getCameraMatrix(const float4 &k)
{
    Matrix4 K;
    K.data[0] = make_float4(k.x, 0, k.z, 0);
    K.data[1] = make_float4(0, k.y, k.w, 0);
    K.data[2] = make_float4(0, 0, 1, 0);
    K.data[3] = make_float4(0, 0, 0, 1);
    return K;
}

inline Matrix4 getInverseCameraMatrix(const float4 &k)
{
    Matrix4 invK;
    invK.data[0] = make_float4(1.0f / k.x, 0, -k.z / k.x, 0);
    invK.data[1] = make_float4(0, 1.0f / k.y, -k.w / k.y, 0);
    invK.data[2] = make_float4(0, 0, 1, 0);
    invK.data[3] = make_float4(0, 0, 0, 1);
    return invK;
}
__global__ void tangent_colors_kernel2(const Patch<Normal> normals, Patch<RGB> colors);


// column pass using coalesced global memory reads
// __global__ void bilateral_filter(Image<float> out, const Image<float> in, const Image<float> gaussian, const float e_d, const int r)
// {
//     const uint2 pos = thr2pos2();

//     if (in[pos] == 0)
//     {
//         out[pos] = 0;
//         return;
//     }

//     float sum = 0.0f;
//     float t = 0.0f;
//     const float center = in[pos];

//     for (int i = -r; i <= r; ++i)
//     {
//         for (int j = -r; j <= r; ++j)
//         {
//             const float curPix = in[make_uint2(clamp(pos.x + i, 0u, in.size.x - 1), clamp(pos.y + j, 0u, in.size.y - 1))];
//             if (curPix > 0)
//             {
//                 const float mod = sq(curPix - center);
//                 const float factor = gaussian[make_uint2(i + r, 0)] * gaussian[make_uint2(j + r, 0)] * __expf(-mod / (2 * e_d * e_d));
//                 t += factor * curPix;
//                 sum += factor;
//             }
//         }
//     }
//     out[pos] = t / sum;
// }
